#!/bin/bash
# House Radar: the Mini's Wi-Fi card (en1) as a listen-only monitor. Root-owned.
# RPM may run exactly "radar-sniff stream" and "radar-sniff off" via sudo (/etc/sudoers.d/house-radar).
# Never touches en0 — the Mini's Ethernet carries Home Control (8787) and Lutron AV (8788).
# Channels + dwell come from files radar writes (digits only are used).
# The stream stops when: "radar-sniff off" runs, Wi-Fi is switched off, radar stops reading (broken pipe), or its parent exits.
set -u
IF=en1
AIRPORT=/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport
RUN=/Users/RPM/HouseRadar/radar/run
# Wi-Fi power before the stream began (root-only dir). "off" puts it back the way
# it was instead of forcing it off: a Mac whose Wi-Fi was on and connected gets it
# back on and reconnected (2026-10-17: forcing off left Pete's iMac offline 24 min).
PRIOR=/var/run/radar-sniff.prior
restore() {
  if [ "$(/bin/cat "$PRIOR" 2>/dev/null)" = "on" ]; then
    /usr/sbin/networksetup -setairportpower "$IF" off; /bin/sleep 1
    /usr/sbin/networksetup -setairportpower "$IF" on      # rejoins its saved network
  else
    /usr/sbin/networksetup -setairportpower "$IF" off
  fi
}
case "${1:-}" in
  off)
    /usr/bin/pkill -f "radar-sniff stream" 2>/dev/null
    /usr/bin/pkill -f "tcpdump -I -i $IF" 2>/dev/null
    restore
    exit 0 ;;
  stream) ;;
  *) echo "usage: radar-sniff stream|off" >&2; exit 2 ;;
esac
# Never listen on the card that carries this Mac's way out: monitor mode drops
# its connection and cuts the whole Mac off. Checked every time, not just at install.
if [ "$(/sbin/route -n get default 2>/dev/null | /usr/bin/awk '/interface:/{print $2}')" = "$IF" ]; then
  echo "REFUSING: $IF is this Mac's only connection - plug in Ethernet to use it as an ear" >&2
  exit 3
fi
PARENT=$PPID
# Remember the power only when no listen is already running (a running one turned it on).
if ! /usr/bin/pgrep -f "tcpdump -I -i $IF" >/dev/null; then
  if /usr/sbin/networksetup -getairportpower "$IF" | /usr/bin/grep -q ": On"; then echo on > "$PRIOR"; else echo off > "$PRIOR"; fi
fi
cleanup() { stop_td 2>/dev/null; /usr/bin/pkill -f "tcpdump -I -i $IF" 2>/dev/null; restore; exit 0; }
trap cleanup EXIT INT TERM HUP PIPE
/usr/sbin/networksetup -setairportpower "$IF" on
sleep 2
"$AIRPORT" "$IF" -z >/dev/null 2>&1
# Mode (radar writes "continuous" or "restart" to sniff-mode):
#   continuous - one tcpdump for the whole stream, channels switched under it.
#                No dead second per channel (a restart per hop left a Mac ear
#                deaf most of the time - 2026-09-27).
#   restart    - the old way: a fresh tcpdump per channel. Radar falls back to
#                it by itself if a Mac hears nothing in continuous mode.
MODE=restart
TD=""
stop_td() { [ -n "$TD" ] && kill "$TD" 2>/dev/null && wait "$TD" 2>/dev/null; TD=""; }
while true; do
  /usr/sbin/networksetup -getairportpower "$IF" | /usr/bin/grep -q ": On" || exit 0   # switched off -> stop
  kill -0 "$PARENT" 2>/dev/null || exit 0                                               # caller gone -> stop
  CH=$(/usr/bin/tr -cd '0-9 ' < "$RUN/sniff-channels" 2>/dev/null)
  [ -z "${CH// /}" ] && CH="48 149 36 44 64 100 132 1 6 11"
  DWELL=$(/usr/bin/tr -cd '0-9.' < "$RUN/sniff-dwell" 2>/dev/null)
  [ -z "$DWELL" ] && DWELL=1.5
  WANT=$(/usr/bin/tr -cd 'a-z' < "$RUN/sniff-mode" 2>/dev/null)
  [ "$WANT" = "continuous" ] || WANT=restart
  if [ "$WANT" != "$MODE" ]; then stop_td; MODE=$WANT; fi
  for c in $CH; do
    [ "$c" -ge 1 ] 2>/dev/null && [ "$c" -le 177 ] || continue
    kill -0 "$PARENT" 2>/dev/null || exit 0
    "$AIRPORT" "$IF" --channel="$c" >/dev/null 2>&1
    echo "CHANNEL $c $(/bin/date +%s)" || exit 0                                          # reader gone -> stop
    if [ "$MODE" = continuous ]; then
      if [ -z "$TD" ] || ! kill -0 "$TD" 2>/dev/null; then
        /usr/sbin/tcpdump -I -i "$IF" -y IEEE802_11_RADIO -e -n -l "not type ctl" 2>/dev/null &
        TD=$!
      fi
      /bin/sleep "$DWELL"
    else
      /usr/sbin/tcpdump -I -i "$IF" -y IEEE802_11_RADIO -e -n -l "not type ctl" 2>/dev/null &
      P=$!
      /bin/sleep "$DWELL"
      kill "$P" 2>/dev/null; wait "$P" 2>/dev/null
    fi
  done
done
